<script setup lang="ts">
import { cloneDeep } from 'lodash-es';
import useEmmit from '@/hooks/useEmitter';
import usePageSave from '@/hooks/usePageSave';
import useSticky from '@/hooks/useSticky';
import { tableConfig } from './config/table-config';
import { formConfig } from './config/form-config';
import baseTable from '@/components/base-ui/base-table/index.vue';
import useDrag from '../../hook/useDrag';
import { doUpdateBom, doGetBomDetail } from '@/api/modeling/bom/index';
import {
    formatInput,
    transformDataToKeys,
    generateId,
    removeDataById,
    flattenArray,
    filterByKey,
    calculateTreeLen,
    hideValidErrText,
    completionNumber
} from '@/utils';
import { Message, Modal } from '@/instance';
import { generateNameToLevel } from '@/config/constant';
import MateriaCode from '../BomList/dialog/materiaCode/index.vue';
import type { MaterialChooseVO, BomItemDTO, BomDataRequest } from '@/api/modeling/bom/model/type';

defineOptions({
    name: 'BOMEdit'
});

useEmmit(); // 页面标签路由关闭二次确认事件监听

interface BomForm {
    [key: string]: string | null;
    materialCode: string | null;
    materialName: string | null;
    bomCode: string | null;
    bomName: string | null;
}

const initBomForm = () => {
    return {
        materialCode: null,
        materialName: null,
        bomName: null,
        bomCode: null
    };
};

const bomForm = ref<BomForm>(initBomForm());
const formItems = ref<ItemConfig[]>(formConfig.items);
const maxBomNum = 10000; // 最大 BOM 添加总数
const route = useRoute();
const bomId = computed(() => route.query.id);
const tableDataLen = computed(() => (tableRef.value && tableRef.value.tableData.length) || 0);

/**
 * 校验相关
 */
const {
    popoverVisible,
    popoverErrMsg,
    handleInput,
    handleFocus,
    handleValidFieldPass,
    showErrorPopover,
    hideErrorPopover,
    resetRelatedValid,
    setValidErrorVisible
} = useValid({
    position: 'left',
    formConfig: formConfig.items,
    formFields: bomForm,
    mountErrId: formConfig.id
});

/**
 * @: 新增提交
 * @param {*} const
 * @return {*}
 */
const { loading, modalOk, modalSlotRef: tableRef } = useModal({ data: { bomForm } });

/**
 * 编辑状态，初始化数据
 */
const initData = async (id: string) => {
    try {
        const { data } = await doGetBomDetail({ id }); // 获取 BOM 详情
        if (data) {
            handleInitEditData(data as unknown as BomDataRequest);
        }
    } catch (error) {
        console.error(error);
    }
};

/**
 * 处理编辑初始化数据
 */
const BomDetailId = ref();
const bomCodeDisabled = ref(false);
const handleInitEditData = (data: BomDataRequest) => {
    BomDetailId.value = data.id; // BOM id
    tableRef.value.setInit(false); // 取消默认表格添加行
    parentMaterialId = data.materialId as string; // 初始化选中父项物料编码 id
    bomCodeDisabled.value = true;

    bomForm.value = {
        ...transformDataToKeys<any>(data, ['bomCode', 'bomName', 'materialCode', 'materialName'])
    };

    materialSelectList = tableRef.value.tableData = transformEditTableData(data.children) as any; // 初始化物料选中数据&物料列表数据

    nextTick(() => {
        setAsyncColumnWidth();
    });
};

/**
 * 递归遍历表格数据，更新字段值
 */
const transformEditTableData = (arr: BomItemDTO[]) => {
    return arr.map(function (obj) {
        obj.unAction = true;
        obj.edit = true;
        if (obj.children && obj.children.length > 0) {
            obj.children = transformEditTableData(obj.children);
        }
        return obj;
    });
};

/**
 * 递归过滤数据
 */
const deepFilterData = (list: BomItemDTO[]) => {
    return list.map(item => {
        if (item.children) item.children = deepFilterData(item.children);

        return transformDataToKeys(item, [
            'level',
            'materialCode',
            'materialName',
            'materialSpecification',
            'productionType',
            'unit',
            'unitUsage',
            'remarks',
            'children',
            'materialId'
        ]);
    });
};

type TableData = MaterialChooseVO & anyObj;

/**
 * 添加 BOM 层级前置校验
 */
const handleBeforeValid = () => {
    const totalTreeNum = calculateTreeLen(tableRef.value.tableData || []);

    if (totalTreeNum >= maxBomNum) {
        Message.error(`最多支持${maxBomNum}行BOM层级数据`);
        return false;
    }

    return true;
};

/**
 * 给 bom 树层加 level 层级字段
 */
const setBomLevel = (list: TableData[], level = 1) => {
    for (let i = 0; i < list.length; i++) {
        list[i].level = level;
        if (list[i].children) {
            setBomLevel(list[i].children, level + 1);
        }
    }
};

/**
 * 点击父项物料编码
 */
const materiaCodeRef = ref();
let isChildren: boolean | null;
const validParentMaterialId = ref(); // 记录当前校验 id 用于清除表格验证
const handleClickParentMaterial = (id: string) => {
    isChildren = null; // 非子项物料
    selection = false; // 非多选
    validParentMaterialId.value = id;
    materiaCodeRef.value.open(parentMaterialId, isChildren);
};

/**
 *选择父项物料编码，点击保存
 */
let parentMaterialId: string | null; // 父项物料 id
const parentMaterialOk = (materialData: MaterialChooseVO) => {
    resetRelatedValid([validParentMaterialId.value]);
    parentMaterialId = materialData.id as string;
    bomForm.value.materialCode = materialData.materialCode!;
    bomForm.value.materialName = materialData.materialName!;
};

/**
 * 点击物料编码 model 确定
 */
const handleMateriaOk = (materialData: MaterialChooseVO) => {
    if (selection) {
        // 多选物料
        const newMateriaData = cloneDeep(materialData as MaterialChooseVO[]).filter(item => {
            // 过滤筛选出表格中没有包含该 id 的物料数据，避免重复渲染物料数据
            const noHasIdItem = tableRef.value.tableData.every(
                (data: MaterialChooseVO) => data.materialId !== item.id
            );

            return noHasIdItem;
        });

        // 转换提交表单物料所需格式
        const transformMateriaData = newMateriaData.map(item => {
            return {
                ...item,
                ...tableRef.value.initAddRow(),
                materialId: item.id
            };
        });

        materialSelectList = tableRef.value.tableData = [
            ...tableRef.value.tableData,
            ...transformMateriaData
        ];

        setBomLevel(tableRef.value.tableData);
    } else {
        // 单选物料
        if (!isChildren) {
            parentMaterialOk(materialData); // 保存父项物料编码
        } else {
            childMaterialOk(materialData); // 保存子项物料编码
        }
    }
};

/**
 * 点击子项物料编码
 */
let materialCodeId: string | null; // 子项物料 id
const selectMaterialId = ref(); // 记录当前选项 id 用于数据填充
const validMaterialId = ref(); // 记录当前校验 id 用于清除表格验证
const validIndex = ref(0);

const handleClickMaterial = (id: string, row: MaterialChooseVO, index: number) => {
    isChildren = true; // 子项物料
    selection = false; // 非多选
    validIndex.value = index;
    validMaterialId.value = id;
    selectMaterialId.value = row.id;
    materialCodeId = row.materialId!;
    materiaCodeRef.value.open(materialCodeId, isChildren);
};

/**
 * 选择子项物料编码，点击保存
 */
const childMaterialOk = (materialData: MaterialChooseVO) => {
    resetRelatedValid([validMaterialId.value], validIndex.value);

    // 过滤出当前所选的 id 项
    const currentOption = filterByKey(tableRef.value.tableData, selectMaterialId.value, 'id');

    if (currentOption) {
        const keys = [
            'id',
            'materialCode',
            'materialName',
            'materialSpecification',
            'productionType',
            'unit'
        ];

        for (let key of keys) {
            if (key === 'id') {
                currentOption['materialId'] = materialData[key];
                continue;
            }
            currentOption[key] = materialData[key];
        }
    }
};

/**
 * 子项物料增加
 */
const handleBomAdd = (row: TableData) => {
    if (!handleBeforeValid()) {
        return;
    }

    if (!row.children) {
        row.children = [];
    }

    // 创建新数据
    const newTableRow: TableData = { id: generateId(), edit: true, unAction: true };

    const fieldKeys = tableConfig.items.map(item => item.modelKey);
    for (let key of fieldKeys) {
        newTableRow[key!] = null; // 排除这俩属性，避免渲染不出输入框
    }
    newTableRow.level = row.level + 1;
    newTableRow.parentId = row.id; // 记录父元素 id 用于后续拖拽元素跟踪
    row.children.push(newTableRow);

    // 更新层级列宽度
    nextTick(() => {
        setAsyncColumnWidth();
    });
};

/**
 * 动态设置列宽
 * @return {*}
 */
let currentLevel = 0;
const levelColumn = tableConfig.items.filter(item => item.modelKey === 'level');
const initColumnWidth = levelColumn[0].columnProps?.width as string;
const setAsyncColumnWidth = () => {
    const flatternTableData = flattenArray(tableRef.value.tableData);
    const maxLevel = Math.max(...flatternTableData.map(item => item.level));
    const maxLevelEdge = 6; // 层级最大临界点

    // 超过规定的最大临界点且当前最大层级与当前设置层级不相等
    if (maxLevel > maxLevelEdge && maxLevel !== currentLevel) {
        currentLevel = maxLevel;
        // 动态设置表头宽度
        tableRef.value.setAsyncColumnWidth(
            'level',
            parseInt(initColumnWidth) + 30 * (maxLevel - maxLevelEdge) // 初始宽 + 递增宽范围 x (最大层级 - 层级最大临界点)
        );
    }
};

/**
 * 删除
 */
const handleBomDel = (row: BomDataRequest) => {
    if (row.children && row.children.length) {
        Modal.confirm({
            title: '确认删除',
            content: '删除当前BOM层级，该层级下的BOM子级会一并删除，请确认操作！',
            onOk: () => {
                removeDataById(tableRef.value.tableData, row.id);
            }
        });
    } else {
        removeDataById(tableRef.value.tableData, row.id);
    }

    // 实时监听表单校验文案提示
    hideValidErrText(() => {
        tableRef.value.errValidRef = null;
    });
};

/**
 * 保存新增数据
 */
const addData = async () => {
    const children = cloneDeep(tableRef.value.tableData);
    const data = {
        ...bomForm.value,
        ...(BomDetailId.value ? { id: BomDetailId.value } : {}),
        materialId: parentMaterialId,
        children: deepFilterData(children)
    } as BomDataRequest;

    const formData = data;

    try {
        const res = await doUpdateBom(formData);

        if (res) {
            Message.success('操作成功！');
        }

        return res;
    } catch (error) {
        console.error(error);
    }
};

/**
 * 点击保存按钮
 */
const handleOk = async () => {
    try {
        const formValid = handleValidFieldPass();
        if (!formValid) return;

        // 表格树数据结构抹平
        const spreadData = flattenArray<any>(tableRef.value.tableData);
        const tableValid = tableRef.value.handleValidFieldPass(spreadData);

        if (!tableValid) return;

        await modalOk(addData, true);
        usePageSave('/modeling');
    } catch (e) {
        console.log(e);
    }
};

/**
 * 多选物料编码
 */
let selection: boolean | null;
let materialSelectList: MaterialChooseVO[] = [];
const handleSelectMaterial = () => {
    selection = true;
    isChildren = true;
    materiaCodeRef.value.open(materialSelectList, isChildren, selection);
};

/**
 * 全屏
 */
const fullscreenRef = ref();
const expand = computed(() => fullscreenRef.value && fullscreenRef.value.expand);
const { detailPageSticky } = useSticky(true); // 头部吸顶

const { tableKey, recordRow, initDragInstance } = useDrag(tableRef);

onMounted(() => {
    initDragInstance();
});

onActivated(() => {
    initData(bomId.value as string);
});
</script>
<template>
    <q-page>
        <div :id="generateNameToLevel('4')" :class="['m-bom__page', { 'is-fullscreen': expand }]">
            <!-- 基础信息 -->
            <div class="m-title">
                <h2 class="m-title--common m-b-8">基础信息</h2>
                <q-fullscreen ref="fullscreenRef" />
            </div>
            <q-form-item :id="formConfig.id!" class="form-placeholder"></q-form-item>
            <el-row :gutter="32" class="m-b-16">
                <el-col
                    v-for="item in formItems"
                    :key="item.validProps?.id"
                    :span="6"
                    class="role-main__header form-common m-b-18"
                >
                    <label class="is-column"><span>*</span>{{ item.columnProps?.label }}:</label>
                    <q-form-item
                        v-if="!item.itemType || item.itemType === 'text'"
                        :id="item.validProps?.id!"
                    >
                        <el-input
                            v-model="bomForm[item.modelKey!]"
                            :placeholder="`请输入${item.columnProps?.label}`"
                            :disabled="item.modelKey === 'bomCode' && bomCodeDisabled"
                            v-bind="item.elInputProps"
                            @focus="
                                (e: any) =>
                                    handleFocus(e, bomForm, {
                                        config: item
                                    })
                            "
                            @keyup="
                                (e: any) => {
                                    bomForm[item.modelKey!] = formatInput(
                                        bomForm,
                                        item.modelKey!,
                                        item.validFormatType,
                                        {
                                            showErrorPopover: (msg: string) => {
                                                showErrorPopover(e, msg);
                                            },
                                            hideErrorPopover
                                        }
                                    );
                                    handleInput(e, bomForm, {
                                        config: item
                                    });
                                }
                            "
                            @blur="setValidErrorVisible(false)"
                        />
                    </q-form-item>
                    <q-form-item v-else-if="item.itemType === 'other'" :id="item.validProps?.id!">
                        <q-virtual-select
                            v-model="bomForm[item.modelKey!]"
                            placeholder="请选择物料编码"
                            @click="handleClickParentMaterial(item.validProps?.id!)"
                        />
                    </q-form-item>
                </el-col>
            </el-row>

            <!-- 子项物料 -->
            <h2 class="m-title--common m-b-16">子项物料</h2>
            <base-table
                ref="tableRef"
                :hide-del="true"
                :close-edit="true"
                :closeDelRow="true"
                action-width="90px"
                :openSticky="true"
                body-row-height="50px"
                :table-config="tableConfig"
                :table-attr="{
                    treeProps: { children: 'children' },
                    defaultExpandAll: true,
                    highlightCurrentRow: false
                }"
                :before-add-valid-fn="handleBeforeValid"
                :class="{ 'is-fullscreen': expand }"
                :show-setting="false"
                :show-page="false"
                :tableKey="tableKey"
            >
                <!-- 选择物料 -->
                <template #tableHeadStart>
                    <el-button type="primary" @click="handleSelectMaterial">选择物料</el-button>
                </template>
                <!-- 序号 -->
                <template #index>
                    <el-table-column type="index" label="序号" width="80">
                        <template #default="{ row, $index }">
                            <div class="f-center">
                                <span
                                    class="drag-handle f-center"
                                    style="margin-right: 6px; cursor: grab"
                                    @mousedown.stop="recordRow(row)"
                                    ><q-icon
                                        icon-class="a-tuozhuai1"
                                        size="21"
                                        color="rgb(140 140 140)"
                                /></span>
                                <span :style="{ color: 'rgb(140,140,140)' }" class="">{{
                                    completionNumber($index + 1)
                                }}</span>
                            </div>
                        </template>
                    </el-table-column>
                </template>
                <!-- BOM 层级 -->
                <template #level="{ row }">
                    <span>{{ row.level || '--' }}</span>
                </template>
                <!-- 子项物料编码 -->
                <template #materialCode="{ row, item, index }">
                    <q-virtual-select
                        v-model="row.materialCode"
                        placeholder="请选择"
                        :clear="false"
                        @click="handleClickMaterial(item.validProps?.id!, row, index)"
                    />
                </template>

                <!-- 子项物料名称 -->
                <template #materialName="{ row }">
                    {{ row.materialName || '--' }}
                </template>

                <!-- 生产类型 -->
                <template #productionType="{ row }">
                    {{ row.productionType || '--' }}
                </template>

                <!-- 规格型号 -->
                <template #materialSpecification="{ row }">
                    {{ row.materialSpecification || '--' }}
                </template>

                <!-- 单位 -->
                <template #unit="{ row }">
                    {{ row.unit || '--' }}
                </template>

                <!-- 操作 -->
                <template #action="{ row }">
                    <el-link
                        type="primary"
                        class="m-action__btn"
                        style="margin-left: 0"
                        @click.stop="handleBomAdd(row)"
                        >增加</el-link
                    >
                    <el-link
                        v-if="tableDataLen > 1 || row.level > 1"
                        type="primary"
                        class="m-action__btn"
                        @click="handleBomDel(row)"
                        >删除</el-link
                    >
                </template>
            </base-table>

            <!-- 校验提示框 -->
            <q-valid-popover
                :popover-visible="popoverVisible"
                :popover-err-msg="popoverErrMsg"
                position="left"
            />

            <!-- 物料编码 -->
            <MateriaCode ref="materiaCodeRef" @ok="handleMateriaOk" />
        </div>
        <q-submit-button
            size="default"
            :hide-label="true"
            align="right"
            :horizontal="true"
            :cancel-button="false"
            :close-pseudo="true"
            :loading="loading"
            class="p-t-8 p-b-8"
            :style="detailPageSticky ? { top: '-48px' } : null"
            @save="handleOk"
        >
        </q-submit-button>
    </q-page>
</template>

<style lang="scss" scoped>
@include b(m-action) {
    @include e(btn) {
        margin-left: 12px;

        &:nth-of-type(1) {
            margin-left: 0;
        }
    }
}

.m-bom__page {
    flex: 1;

    :deep() {
        .m-table__header {
            position: sticky;
            top: 55px;
            z-index: 11;
            width: 100%;
        }

        .header-sticky .el-table__header-wrapper {
            top: 95px;
        }

        .is-fullscreen {
            .m-table__header {
                top: -7px;
            }

            .header-sticky .el-table__header-wrapper {
                top: 32px;
            }
        }

        .m-table__errMsg {
            position: sticky;
            top: 88px;
            z-index: 111;
        }

        &.is-fullscreen {
            .m-table__errMsg {
                top: 25px;
            }
        }
    }
}
</style>
